package sign_md5

import (
	"ydd-im/config"
	"ydd-im/service/api/models"
	"ydd-im/util/go-util/md5"
	timeUtil "ydd-im/util/go-util/time"
	"ydd-im/util/response"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

var AppSecret string

// MD5 组合加密
func SetUp() gin.HandlerFunc {
	return func(c *gin.Context) {
		sign, err := verifySign(c)
		if sign != nil {
			response.ResponseJsonOk(c,response.Response{
				Code:response.SuccessCode,
				Message:"Ok",
				Data: sign,
			})
			c.Abort()
			return
		}
		if err != nil {
			response.ResponseJsonFail(c,response.Response{
				Code:response.ErrorCode,
				Message:err.Error(),
			})
			c.Abort()
			return
		}
		c.Next()
	}
}

// 验证签名
func verifySign(c *gin.Context) (map[string]string, error) { //  c.Request.Form参数只获取get参数，无法获取post
	_ = c.Request.ParseForm()
	// 将get与post提交c的参数换成header,增加安全性
	c.Request.Form.Del("debug")
	c.Request.Form.Del("appkey")
	c.Request.Form.Del("ts")
	c.Request.Form.Del("sign")
	// 增加header提交的参数
	headerdebug :=c.Request.Header.Get("debug")
	if len(headerdebug)>0 {
		c.Request.Form.Add("debug",headerdebug)
	}
	headerak :=c.Request.Header.Get("appkey")
	if len(headerak)>0 {
		c.Request.Form.Add("appkey",headerak)
	}
	headersn :=c.Request.Header.Get("sign")
	if len(headersn)>0 {
		c.Request.Form.Add("sign",headersn)
	}

	headerts :=c.Request.Header.Get("ts")
	if len(headerts)>0 {
		c.Request.Form.Add("ts",headerts)
	}
	req   := c.Request.Form
	debug := strings.Join(c.Request.Form["debug"], "")
	appkey    := strings.Join(c.Request.Form["appkey"], "")
	sign    := strings.Join(c.Request.Form["sign"], "")
	ts    := strings.Join(c.Request.Form["ts"], "")
	// 验证数据库，判断ak的可靠性，不是的直接选用默认的
	IsPublicSign :=config.GetApiConfig().ApiBase.IsPublicSign
	if !IsPublicSign { // 查询相关的数据库，存在选用对应的秘钥
	    signconfigure := new(models.Signconfigure)
		signconfigureInfo,err :=signconfigure.GetSignconfigureInfo(appkey,"md5")
		if err != nil {
			return nil, errors.New(err.Error())
		}
		if signconfigureInfo.Id ==0 { // 不存在的选用测试的
			// 验证来源
			value, ok := config.ApiAuthConfig[appkey]
			if ok {
				AppSecret = value["md5"]
			} else {
				return nil, errors.New("appkey Error")
			}
		}else{ // 使用私发的appkey
			AppSecret = signconfigureInfo.AppSecret
		}
	}else{
		// 验证来源
		value, ok := config.ApiAuthConfig[appkey]
		if ok {
			AppSecret = value["md5"]
		} else {
			return nil, errors.New("appkey Error")
		}
	}
	if debug == "1" {//创建签名
		currentUnix := timeUtil.GetCurrentUnix()
		// 保证签名传输的时间戳一致,颁发签名的时候没有存入时间戳
		req.Set("ts", strconv.FormatInt(currentUnix, 10))
		res := map[string]string{
			"ts": strconv.FormatInt(currentUnix, 10),
			"sign": createSign(req),
		}
		return res, nil
	}
	// 验证过期时间
	timestamp := time.Now().Unix()
	exp, _    := strconv.ParseInt(config.AppSignExpiry, 10, 64)
	tsInt, _  := strconv.ParseInt(ts, 10, 64)
	// 请求时间在时间范围内，客户端时间防止不一致
	if timestamp - tsInt >= exp {
		fmt.Println("签名过期",timestamp,tsInt,exp)
		return nil, errors.New("签名时间已过期")
	}
	// 验证签名
	if sign == "" || sign != createSign(req) {
		return nil, errors.New("签名错误")
	}

	return nil, nil
}

// 创建签名
func createSign(params url.Values) string {
	// 自定义 MD5 组合
	return md5.MD5(AppSecret + createEncryptStr(params) + AppSecret)
}

func createEncryptStr(params url.Values) string {
	var key []string
	var str = ""
	for k := range params {
		if k != "sign" && k != "debug" {
			key = append(key, k)
		}
	}
	sort.Strings(key)
	for i := 0; i < len(key); i++ {
		var val string
		if (len(params[key[i]])>1){
			val = strings.Join(params[key[i]],",")
		}else{
			val = params.Get(key[i])
		}
		if i == 0 {
			str = fmt.Sprintf("%v=%v", key[i], val)
		} else {
			str = str + fmt.Sprintf("&%v=%v", key[i], val)
		}
	}

	fmt.Println(str)
	return str
}
